﻿using MultiLang;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Windows.Forms;

using BLToolkit.Data;
using BLToolkit.Data.Linq;

using JetBrains.Annotations;

using Rsdn.Janus.DataModel;
using Rsdn.Janus.Properties;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
    public static class ForumsSubscriptionHelper
    {
        public static void UpdateForumPriority(
            IServiceProvider provider,
            int forumId,
            int newPriority)
        {
			using (var db = provider.CreateDBContext())
                db
                    .SubscribedForums(f => f.ID == forumId)
                        .Set(_ => _.Priority, newPriority)
                    .Update();
        }

        public static IQueryable<T> GetAllForums<T>(
			IDataContext mgr,
            Expression<Func<IServerForum, T>> srvSelector,
            Expression<Func<ISubscribedForum, T>> subsSelector)
        {
			var subsFrms = mgr.SubscribedForums();
            return
				mgr
                    .ServerForums()
                    .Where(f => !subsFrms.Any(of => of.ID == f.ID))
                    .Select(srvSelector)
                    .Union(subsFrms.Select(subsSelector));
        }

        /// <summary>
        /// Updates the forums subscriptions.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="requests">The requests.</param>
        /// <param name="promptToDelete">if set to <c>true</c> [prompt to delete].</param>
        /// <exception cref="System.ArgumentNullException">serviceProvider is <c>null</c>.</exception>
        public static void UpdateForumsSubscriptions(
            [NotNull] IServiceProvider serviceProvider,
            [NotNull] IEnumerable<ForumSubscriptionRequest> requests,
            bool promptToDelete)
        {
            Control mainWindowControl = null;
            IUIShell uiShell = serviceProvider.GetService<IUIShell>();
            if (uiShell != null)
            {
                mainWindowControl = uiShell.GetMainWindowParent() as Control;

                // Clear active messages
                uiShell.ClearActiveFeatureView();

                // don't work
                //ListView activeGrid = uiShell.ActiveGrid;
                //activeGrid.Clear();
            }

            Action<IProgressVisualizer> progressAction =
                delegate(IProgressVisualizer progressVisualizer)
                    {
                        progressVisualizer.SetProgressText(ml.ml_string(10, "Обновление подписки на форум(ы)"));
                        //Resources.SubscribeMessage
                        DeleteMessagesPredicate deleteMessagesPredicate =
                            delegate(int id, string name, string description)
                                {
                                    DialogResult dialogResult = MessageBox.Show(
                                        mainWindowControl,
                                        Resources.WarningOnUnsubscribeForum.FormatStr(description),
                                        Resources.WarningOnUnsubscribeForumCaption,
                                        MessageBoxButtons.YesNo,
                                        MessageBoxIcon.Question);

                                    return promptToDelete && dialogResult == DialogResult.Yes;
                                };

                        DeleteMessagesPredicate mainDeleteMessagesPredicate =
                            delegate(int id, string name, string description)
                                {
                                    if (mainWindowControl != null && mainWindowControl.InvokeRequired)
                                    {
                                        return (bool) mainWindowControl.Invoke(
                                            new Func<bool>(() => deleteMessagesPredicate(id, name, description))
                                        );
                                    }
                                    return deleteMessagesPredicate(id, name, description);
                                };

                        UpdateForumsSubscriptions(serviceProvider, requests, mainDeleteMessagesPredicate);
                    };

            ProgressWorker.Run(serviceProvider, false, progressAction);
        }

        private delegate bool DeleteMessagesPredicate(int id, string name, string descript);

        /// <summary>
        /// Subscribes to the specified forum.
        /// pure db
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="forumId">The <c>forumId</c>.</param>
        private static void Subscribe(IServiceProvider provider, int forumId)
        {
			using (var db = provider.CreateDBContext())
            {
                IQueryable<IServerForum> serverForums = db.ServerForums(f => f.ID == forumId);

                serverForums
                    .Into(db.SubscribedForums())
                    .Value(_ => _.ID, f => f.ID)
                    .Value(_ => _.Name, f => f.Name)
                    .Value(_ => _.Descript, f => f.Descript)
                    .Value(_ => _.LastSync, f => -1)
                    .Insert();
            }
        }

        /// <summary>
        /// Unsubscribe the specified forum
        /// Pure db
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="forumId">The forum id.</param>
        /// <param name="deleteMessages">if set to <c>true</c> [delete messages].</param>
        private static void Unsubscribe(IServiceProvider provider, int forumId, bool deleteMessages)
        {
            using (var db = provider.CreateDBContext())
            {
                using (var tx = db.BeginTransaction())
                {
                    if (deleteMessages)
                    {
                        db.TopicInfos(ti => ti.ForumID == forumId).Delete();
                        db.Messages(m => m.ForumID == forumId).Delete();
                    }

                    // Почему когда отписываешься от форума (неважно удаляются ли сообщения), 
                    // из Избранного удаляются ссылки на те сообщения, которые соответствуют форуму?
                    //db.Execute(@"
                    //	DELETE FROM [favorites]
                    //	WHERE
                    //		[favorites].[mid] IN
                    //			(SELECT [mid] FROM [messages]
                    //				WHERE [messages].[gid] = @gid)",
                    //	"@gid", fid);

                    db.SubscribedForums(f => f.ID == forumId).Delete();
                    tx.Commit();
                }
            }
        }

        /// <summary>
        /// Updates the forums subscriptions.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="requests">The requests.</param>
        /// <param name="deleteMessagesPredicate">The delete messages predicate.</param>
        /// <exception cref="ArgumentNullException"><paramref name="serviceProvider"/> is <c>null</c>.</exception>
        private static void UpdateForumsSubscriptions(
            [NotNull] IServiceProvider serviceProvider,
            [NotNull] IEnumerable<ForumSubscriptionRequest> requests,
            [NotNull] DeleteMessagesPredicate deleteMessagesPredicate)
        {
            if (serviceProvider == null)
            {
                throw new ArgumentNullException("serviceProvider");   //MLHIDE
            }
            if (requests == null)
            {
                throw new ArgumentNullException("requests");          //MLHIDE
            }
            if (deleteMessagesPredicate == null)
            {
                throw new ArgumentNullException("deleteMessagesPredicate"); //MLHIDE
            }

			using (var mgr = serviceProvider.CreateDBContext())
            {
                var forums =
                    GetAllForums(
							mgr,
                            f => new { f.ID, f.Name, f.Descript, Subscribed = false },
                            f => new { f.ID, f.Name, f.Descript, Subscribed = true })
                        .ToDictionary(forum => forum.ID);


                ForumSubscriptionRequest[] subscriptionRequests =
                    requests
                        .Where(
                            delegate(ForumSubscriptionRequest request)
                                {
                                    bool ret = false;
                                    if (forums.ContainsKey(request.ForumId))
                                    {
                                        ret = request.IsSubscribed != forums[request.ForumId].Subscribed;
                                    }

                                    return ret;
                                })
                        .ToArray();

                if (!subscriptionRequests.Any())
                {
                    return;
                }

                const ForumEntryChangeType FORUM_ENTRY_CHANGE_TYPE =
                    ForumEntryChangeType.ForumSubscription;

                IEventBroker eventBroker = serviceProvider.GetRequiredService<IEventBroker>();

                IEnumerable<ForumEntryIds> forumEntryIds = subscriptionRequests.Select(request => new ForumEntryIds(request.ForumId));
                ForumEntryIds[] changedForumIds = forumEntryIds.ToArray();
                
                ForumEntryChangedEventArgs forumEntryChangedEventArgs = 
                    new ForumEntryChangedEventArgs(changedForumIds, FORUM_ENTRY_CHANGE_TYPE);

                eventBroker.Fire(ForumEventNames.BeforeForumEntryChanged, forumEntryChangedEventArgs);

                foreach (var request in subscriptionRequests)
                {
                    if (request.IsSubscribed)
                    {
                        Subscribe(serviceProvider, request.ForumId);
                    }
                    else
                    {
                        var forum = forums[request.ForumId];

                        // Fearute not defined here but it will be good to clear active feature
                        //Rsdn.Janus.ObjectModel.Features.Instance.ActiveFeature = null
                        Unsubscribe(
                            serviceProvider,
                            request.ForumId,
                            deleteMessagesPredicate(forum.ID, forum.Name, forum.Descript));
                    }
                }

                // Вызываем обновление данных
                IUIShell uiShell = serviceProvider.GetRequiredService<IUIShell>();

                AsyncOperation asyncOperation = uiShell.CreateUIAsyncOperation();

                asyncOperation.PostOperationCompleted(() => uiShell.RefreshData(serviceProvider));

				// TODO AN Проверить нет ли здесь также обновления данных
                eventBroker.Fire(ForumEventNames.AfterForumEntryChanged, forumEntryChangedEventArgs);
            }
        }

        /// <summary>
        /// Determines whether the specified forum id is trash forum id.
        /// </summary>
        /// <param name="forumId">The forum id.</param>
        /// <returns>
        /// 	<c>true</c> if the specified forum id is trash forum id; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsTrashForum(int forumId)
        {
            return forumId == 0 || forumId == 58;
        }
    }
}